package co.za.alacrity.suduko;

import co.za.alicrity.exactcover.Node;

import java.util.HashMap;
import java.util.List;

import co.za.alicrity.exactcover.ExactCoverMatrix;




public class SudokuGame extends ExactCoverMatrix{
	
	private int[][] grid;
	private boolean[][] exactGrid;
	
	public SudokuGame(int[][] grid){
		this.grid=grid;
		
		generate();
	}
	
	public boolean[] mapGridCellToRow(int row,int col,int value){
		boolean[] ret = new boolean[324];
		for (int r=0;r<ret.length;r++){
			ret[r]=false;
		}
		
		ret[(col)+(row*9)]=true;
		ret[81+(value-1)+(row*9)]=true;
		ret[162+(value-1)+(col*9)]=true;
		ret[243+(value-1)+(getBlockNumber(row, col)*9)]=true;
		
		return ret;
	}
	
	public int getBlockNumber(int row, int col){
		if (row<3){
			if (col<3) return 0;
			if (col<6) return 1;
			else return 2;
		}
		if (row<6){
			if (col<3) return 3;
			if (col<6) return 4;
			else return 5;
		} else {
			if (col<3) return 6;
			if (col<6) return 7;
			else return 8;
		}
	}
	
	

	@Override
	public boolean getAt(int row, int col) {
		return exactGrid[row][col];
	}

	@Override
	public int getRows() {
		return exactGrid.length;
	}

	@Override
	public int getCols() {
		return exactGrid[0].length;
	}



	@Override
	public void applySolution(List<Integer> rowNumbers) {
		for (Integer cur : rowNumbers){
			grid[getRowFromSolution(exactGrid[cur])][getColFromSolution(exactGrid[cur])]=getValFromSolution(exactGrid[cur]);
		}
		
	}
	
	private int getRowFromSolution(boolean[] solutionRow){
		for (int r=0;r<81;r++){
			if (solutionRow[r]){
				return r/9;
			}
		}
		return -1;
	}
	
	private int getColFromSolution(boolean[] solutionRow){
		for (int r=0;r<81;r++){
			if (solutionRow[r]){
				return r%9;
			}
		}
		return -1;
	}
	
	private int getValFromSolution(boolean[] solutionRow){
		for (int r=81;r<162;r++){
			if (solutionRow[r]){
				return (r%9)+1;
			}
		}
		return -1;
	}
	public String toString(){
		String n="";
		for (int r=0;r<13;r++){
			n+="#";
		}
		n+="\n";
		
		
		for (int r=0;r<3;r++){
			n+="#";
			for (int k=0;k<9;k++){
				n+=grid[r][k];
				if ((k+1)%3==0) n+="#";
			}
			n+="\n";
		}
		
		
		for (int r=0;r<13;r++){
			n+="#";
		}
		n+="\n";
		
		
		
		for (int r=3;r<6;r++){
			n+="#";
			for (int k=0;k<9;k++){
				n+=grid[r][k];
				if ((k+1)%3==0) n+="#";
			}
			n+="\n";
		}
		
		
		
		for (int r=0;r<13;r++){
			n+="#";
		}
		n+="\n";
		
		
		
		
		for (int r=6;r<9;r++){
			n+="#";
			for (int k=0;k<9;k++){
				n+=grid[r][k];
				if ((k+1)%3==0) n+="#";
			}
			n+="\n";
		}
		
		for (int r=0;r<13;r++){
			n+="#";
		}
		
		
		return n;
		
	}

	public int getSudokuValue(int r, int k) {
		return grid[r][k];
	}

	public void setValue(int row, int col, int intValue) {
		grid[row][col]=intValue;
		
		generate();
	}
	
	private void generate(){
		int zeroCells=0;
		
		for (int r=0;r<9;r++){
			for (int k=0;k<9;k++){
				if (grid[r][k]==0){
					zeroCells++;
				}
			}
		}
		
		int rows = (zeroCells*9)+(81-zeroCells);
		
		exactGrid = new boolean[rows][];
		
		int rowcounter=0;
		for (int r=0;r<9;r++){
			for (int k=0;k<9;k++){
				if (grid[r][k]!=0){
					exactGrid[rowcounter++] = mapGridCellToRow(r, k, grid[r][k]);
				} else {
					for (int z = 1;z<=9;z++){
						exactGrid[rowcounter++] = mapGridCellToRow(r, k, z);
					}
				}
			}
		}
	}
	
	public SudokuGame clone(){
		int[][] ret = new int[9][];
		for (int r=0;r<9;r++){
			ret[r] = new int[9];
			for (int k=0;k<9;k++){
				ret[r][k]=grid[r][k];
			}
		}
		
		return new SudokuGame(ret);
	}
	
	public boolean won(){
		
		for (int r=0;r<9;r++){
			HashMap<Integer, Integer> checks = new HashMap<Integer, Integer>();
			for (int k=0;k<9;k++){
				checks.put(grid[r][k], grid[r][k]);
			}
			for (int z=1;z<=9;z++){
				if (checks.get(z)==null) return false;
			}
		}
		
		for (int k=0;k<9;k++){
			HashMap<Integer, Integer> checks = new HashMap<Integer, Integer>();
			for (int r=0;r<9;r++){
				checks.put(grid[r][k], grid[r][k]);
			}
			for (int z=1;z<=9;z++){
				if (checks.get(z)==null) return false;
			}
		}
		
		for (int br=0;br<3;br++){
			for (int bk=0;bk<3;bk++){
				
				HashMap<Integer, Integer> checks = new HashMap<Integer, Integer>();
				
				for (int r=0;r<3;r++){
					for (int k=0;k<3;k++){
						checks.put(grid[(br*3)+r][(bk*3)+k], grid[(br*3)+r][(bk*3)+k]);
					}
				}
				
				for (int z=1;z<=9;z++){
					if (checks.get(z)==null) return false;
				}
				
			}
		}
		
		return true;
	}

}
